﻿namespace lighttool.tsbb
{
    //将tsbb的节点编译为代码,这里
    export class ShaderCompiler
    {
        compile(player: tsbb.bbplayer, params: string[]): string
        {
            //step0，根据params 将表达式树构建出来，表达式树会存在公用节点
            //前置插入
            //存在公用节点就会在表达式中计算两次？noway，应该变成变量引用，这是个改写
            //不做改写，也能做倒置的合成
            var out = player.getBeginBlock();
            var c = new Context(this);
            c.player = player;

            //预编译标记重合项目
            //添加临时变量，逻辑表达式这不是问题，后置if而已
            //this.prebuild(c, out);

            var code = this.buildCode(c, out);
            return c.buildContextCode() + "\n" + code;
        }


        buildCodeInParam(c: Context, block: bBlock, inparam: number): string
        {
            var bid = block.inparams[inparam].blockID;
            if (bid >= 0)
            {//表达式弄个变量
                //这里可以弄一个判断，哪些走变量，哪些走嵌入
                //或者根据bid的某个属性判断
                //目前不判断也无伤大雅
                var name = c.tempBlock(bid);

                return name;
                //this.buildCode(c, c.player.getBlock(bid));
            }
            else
            {//常量不管
                var v = block.inparams[inparam].constValue;
                if (v == null)
                    return "null";
                else
                    return v;
            }
        }
        //这里有固定代码，可以结构优化
        buildCode(c: Context, block: bBlock): string
        {
            c.beforeBlock(block);

            if (block.name == "return" && block.type == bBlockType.FlowBlock_User)//return flow.
            {
                var outstr = "return ";
                var p = this.buildCodeInParam(c, block, 0);
                return outstr + p + ";";
            }
            if (block.type == bBlockType.Block_Expr)
            {
                if (block.name == "addone")
                {
                    var p0 = this.buildCodeInParam(c, block, 0);
                    return "calladdone(" + p0 + ")";
                }
                else if (block.name == "add")
                {
                    var p0 = this.buildCodeInParam(c, block, 0);
                    var p1 = this.buildCodeInParam(c, block, 1);
                    return "(" + p0 + "+" + p1 + ")";
                }
            }
            return null;
        }
    }
    export class Context
    {
        player: bbplayer;
        beforeCode: string[] = [];
        beforeTypes: { [id: string]: number } = {};
        tempValueCode: string[] = [];
        tempTypes: { [id: string]: number } = {};
        tempExpr: { [id: number]: string } = {};
        com: ShaderCompiler;
        constructor(com: ShaderCompiler)
        {
            this.com = com;
        }
        buildContextCode(): string
        {
            return this.beforeCode.join("\n") + "\n" + this.tempValueCode.join("\n");
        }
        //这里有固定代码，可以结构优化
        beforeBlock(block: bBlock)
        {
            if (this.beforeTypes[block.name] != null)
                return;
            this.beforeTypes[block.name] = 1;
            if (block.name == "addone")
            {
                this.beforeCode.push("float calladdone(float a)\n{\n    return a+1;\n}\n");
            }
        }
        tempBlock(bid: number)
        {
            if (this.tempExpr[bid] != null)
                return this.tempExpr[bid];
            var code = this.com.buildCode(this, this.player.getBlock(bid));

            var bb = this.player.getBlock(bid);
            var type = bb.getReturnType();
            var number = this.tempTypes[type];
            if (number == null) 
            {
                number = 1;
            }
            else
            {
                number++;
            }
            this.tempTypes[type] = number;


            var name = type + number.toString();
            this.tempExpr[bid] = name;

            this.tempValueCode.push(type + " " + name + "=" + code + ";");
            return name;
        }
    }
    //export enum tagType
    //{
    //    Insert_WithParam,
    //}
    //export class insertTag
    //{
    //    type: tagType;
    //    tag: string[];
    //    tagparam: string[];
    //    inparams: paramTag[];
    //    outparam: paramTag;
    //}
    //export enum tagRef
    //{
    //    REF_Value,
    //    REF_REF,
    //}
    //export class paramTag
    //{
    //    name: string;
    //    type: string;
    //    ref: tagRef;
    //}


}